using System;
using System.Collections.Generic;

namespace SharpFastCGI.Internals
{
	internal class HybridDictionary : IDictionary<string,string>
	{
		/// <value>Typical keys given index.</value>
		private string[] mTypicalKeys;
		/// <value>Value of a typical key given index.</value>
		private string[] mValueOfTypicalKeys;
		/// <value>Indices of typical keys whose value have been set.</value>
		private int[] mTypicalKeysSet;
		/// <value>Number of typical keys whose value have been set.</value>
		private int mTypicalKeysSetCount;
		/// <value>Index of key given key.</value>
		private Dictionary<string,int> mTypicalKeyIndex;
		/// <value>Value given key for non-typical keys.</value>
		private LightDictionary<string,string> mOtherKeys;
		
		public int Count
		{
			get { return mTypicalKeysSetCount + mOtherKeys.Count; }
		}

		public bool IsReadOnly
		{
			get { return false; }
		}

		public ICollection<string> Keys
		{
			get { throw new NotImplementedException(); }
		}

		public ICollection<string> Values
		{
			get { throw new NotImplementedException(); }
		}

		public string this[string Key]
		{
			get
			{
				string ret;
				int idx;
				if(mTypicalKeyIndex.TryGetValue(Key, out idx))
				{
					ret = mValueOfTypicalKeys[idx];
					if(ret != null) return ret;
					throw new KeyNotFoundException();
				}
				else
				{
					return mOtherKeys[Key];
				}
			}
			set
			{
				if(Key == null) throw new ArgumentNullException("Key");
				if(value == null) throw new ArgumentNullException("value");
				
				int idx;
				if(mTypicalKeyIndex.TryGetValue(Key, out idx))
				{
					if(mValueOfTypicalKeys[idx] == null)
					{
						mTypicalKeysSet[mTypicalKeysSetCount++] = idx;
					}
					mValueOfTypicalKeys[idx] = Value;
				}
				else
				{
					mOtherKeys.AddWithoutCheck(new KeyValuePair<string,string>(Key, Value));
				}
			}
		}
		
		public HybridDictionary(string[] TypicalKeys, string[] ValueOfTypicalKeys, int[] TypicalKeysSet, Dictionary<string,int> TypicalKeyIndex, LightDictionary<string,string> OtherKeys)
		{
			mTypicalKeys = TypicalKeys;
			mValueOfTypicalKeys = ValueOfTypicalKeys;
			mTypicalKeysSet = TypicalKeysSet;
			mTypicalKeysSetCount = 0;
			mTypicalKeyIndex = TypicalKeyIndex;
			mOtherKeys = OtherKeys;
		}
		
		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}

		public IEnumerator<KeyValuePair<string,string>> GetEnumerator()
		{
			for(int i = 0, i_up = mTypicalKeysSetCount ; i < i_up ; ++i)
			{
				int idx = mTypicalKeysSet[i];
				yield return new KeyValuePair<string,string>(mTypicalKeys[idx], mValueOfTypicalKeys[idx]);
			}
			foreach(KeyValuePair<string,string> item in mOtherKeys)
				yield return item;
		}
		
		public void Add(string Key, string Value)
		{
			if(Key == null) throw new ArgumentNullException("Key");
			if(Value == null) throw new ArgumentNullException("Value");
			
			int idx;
			if(mTypicalKeyIndex.TryGetValue(Key, out idx))
			{
				if(mValueOfTypicalKeys[idx] != null) throw new ArgumentException("Key duplication.");
				mValueOfTypicalKeys[idx] = Value;
				mTypicalKeysSet[mTypicalKeysSetCount++] = idx;
			}
			else
			{
				mOtherKeys.Add(new KeyValuePair<string,string>(Key, Value));
			}
		}
		
		public void Add(KeyValuePair<string,string> Item)
		{
			if(Key == null) throw new ArgumentException("Key value of <Item> is a null reference.");
			
			Add(Item.Key, Item.Value);
		}

		public void Clear()
		{
			for(int i = 0, i_up = mTypicalKeysSetCount ; i < i_up ; ++i)
			{
				int idx = mTypicalKeysSet[i];
				mValueOfTypicalEnvVar[idx] = null;
			}
			mTypicalKeysSetCount = 0;
			mOtherEnvVar.Clear();
		}

		public bool Contains(KeyValuePair<string,string> Item)
		{
			if(Key == null) throw new ArgumentNullException("Key");
			
			int idx;
			if(mTypicalKeyIndex.TryGetValue(Item.Key, out idx))
				return mValueOfTypicalKeys[idx].Equals(Item.Value);
			else
				return mOtherKeys.Contains(Item);
		}

		public bool ContainsKey(string Key)
		{
			if(Key == null) throw new ArgumentNullException("Key");
			
			int idx;
			if(mTypicalKeyIndex.TryGetValue(Key, out idx))
				return mValueOfTypicalKeys[idx] != null;
			else
				return mOtherKeys.ContainsKey(Key);
		}
		
		public bool TryGetValue(string Key, out string Value)
		{
			if(Key == null) throw new ArgumentNullException("Key");
			
			int idx;
			if(mTypicalKeyIndex.TryGetValue(Key, out idx))
			{
				Value = mValueOfTypicalKeys[idx];
				return Value != null;
			}
			else
				return mOtherKeys.TryGetValue(Key, out Value);
		}

		public void CopyTo(KeyValuePair<string,string>[] Array, int Offset)
		{
			if(Key == null) throw new ArgumentNullException("Array");
			if(Offset < 0) throw new ArgumentException("<Offset> is negative.");
			
			foreach(KeyValuePair<TKey,TValue> item in this)
			{
				Array[Offset++] = item;
			}
		}

		public bool Remove(KeyValuePair<string,string> Item)
		{
			if(Key == null) throw new ArgumentException("Key value of <Item> is a null reference.");
			
			int idx;
			if(mTypicalKeyIndex.TryGetValue(Item.Key, out idx))
			{
				if(!mValueOfTypicalKeys[idx].Equals(Item.Value)) return false;
				mValueOfTypicalKeys[idx] = null;
				return true;
			}
			else
				return mOtherKeys.Remove(Item);
		}
		
		public bool Remove(string Key)
		{
			if(Key == null) throw new ArgumentNullException("Key");
			
			int idx;
			if(mTypicalKeyIndex.TryGetValue(Key, out idx))
			{
				bool ret = mValueOfTypicalKeys[idx] != null;
				mValueOfTypicalKeys[idx] = null;
				return ret;
			}
			else
				return mOtherKeys.Remove(Key);
		}
	}
}
